package cn.edu.fzu.delivery.service.impl;

import cn.edu.fzu.delivery.cache.IpCache;
import cn.edu.fzu.delivery.cache.NetworkCache;
import cn.edu.fzu.delivery.common.RedisClient;
import cn.edu.fzu.delivery.common.RedisKey;
import cn.edu.fzu.delivery.common.Sn;
import cn.edu.fzu.delivery.domain.dto.CourierDeliveryPathDto;
import cn.edu.fzu.delivery.domain.entity.Address;
import cn.edu.fzu.delivery.domain.entity.Area;
import cn.edu.fzu.delivery.domain.entity.City;
import cn.edu.fzu.delivery.domain.entity.Courier;
import cn.edu.fzu.delivery.domain.entity.CourierAddress;
import cn.edu.fzu.delivery.domain.entity.CourierDelivery;
import cn.edu.fzu.delivery.domain.entity.Customer;
import cn.edu.fzu.delivery.domain.entity.Network;
import cn.edu.fzu.delivery.domain.entity.Province;
import cn.edu.fzu.delivery.domain.entity.SysUser;
import cn.edu.fzu.delivery.domain.enums.CourierStatusEnum;
import cn.edu.fzu.delivery.domain.enums.CourierTypeEnum;
import cn.edu.fzu.delivery.domain.enums.DeliveryStatusEnum;
import cn.edu.fzu.delivery.domain.enums.UrgencyEnum;
import cn.edu.fzu.delivery.exception.ServiceException;
import cn.edu.fzu.delivery.helper.DingDingRobot;
import cn.edu.fzu.delivery.helper.EmailHelper;
import cn.edu.fzu.delivery.helper.QRCodeHelper;
import cn.edu.fzu.delivery.mapper.CourierAddressMapper;
import cn.edu.fzu.delivery.mapper.CourierMapper;
import cn.edu.fzu.delivery.service.CourierDeliveryService;
import cn.edu.fzu.delivery.service.customer.AddressService;
import cn.edu.fzu.delivery.service.CourierService;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.io.ByteArrayOutputStream;
import java.math.BigDecimal;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

/**
 * 快递业务层
 * 
 * @author fangxin
 * @date 2022/11/8
 */
@Service
public class CourierServiceImpl implements CourierService {

    private static final String    COURIER_STATUS_EMAIL_TITLE   = "物流状态通知";
    private static final String    COURIER_STATUS_EMAIL_CONTENT = "亲爱的顾客，您的快递状态已更新: ";

    //二维码邮箱展示形式
    private static final String    QRCODE_CONTENT               = "</br>签收二维码，请妥善保存:</br><img src='data:image/png;base64,__IMAGE_CODE__'/>";

    //二维码替换宏
    private static final String    IMAGE_CODE                   = "__IMAGE_CODE__";

    //二维码扫码访问api
    private static final String    QRCODE_API                   = "http://101.43.44.19/api/customer/delivery/receive?courierId=";

    //邮件加急按钮
    private static final String    URGE_BUTTON                  = "</br><a href='http://101.43.44.19/api/customer/delivery/urgeByEmail?courierId=__COURIER_ID__''>__BUTTON__</a>";

    //快递单号替换宏
    private static final String    COURIER_ID                   = "__COURIER_ID__";

    //邮件加急按钮名称替换宏
    private static final String    BUTTON                       = "__BUTTON__";

    @Resource
    private CourierMapper          courierMapper;
    @Resource
    private CourierAddressMapper   courierAddressMapper;
    @Resource
    private AddressService         addressService;
    @Resource
    private EmailHelper            emailHelper;
    @Resource
    private CourierDeliveryService courierDeliveryService;
    @Resource
    private NetworkCache           networkCache;
    @Resource
    private RedisClient            redisClient;

    @Override
    public List<Courier> listCustomerSend(String phone, List<String> courierIds, CourierTypeEnum type,
                                          CourierStatusEnum status, UrgencyEnum urgency, String startTime,
                                          String endTime) {
        List<Courier> list = null;
        if (StringUtils.isNotBlank(phone)) {
            //查询出快递地址中包含该手机号的所有快递地址信息
            List<Long> addressIdList = courierAddressMapper.listIdByPhone(phone);
            if (CollectionUtils.isEmpty(addressIdList)) {
                return Collections.emptyList();
            }
            list = courierMapper.list(addressIdList, null, type, status, urgency, startTime, endTime);
            if (!CollectionUtils.isEmpty(list) && !CollectionUtils.isEmpty(courierIds)) {
                list.removeIf(courier -> !courierIds.contains(courier.getCourierId()));
            }
            return list;
        }
        if (CollectionUtils.isEmpty(courierIds)) {
            return Collections.emptyList();
        }
        return courierMapper.listByCourierIds(courierIds);
    }


    @Override
    public List<Courier> listCustomerReceive(String phone, List<String> courierIds, CourierTypeEnum type,
                                             CourierStatusEnum status, UrgencyEnum urgency, String startTime,
                                             String endTime) {
        List<Courier> list = null;
        if (StringUtils.isNotBlank(phone)) {
            //查询出快递地址中包含该手机号的所有快递地址信息
            List<Long> addressIdList = courierAddressMapper.listIdByPhone(phone);
            if (CollectionUtils.isEmpty(addressIdList)) {
                return Collections.emptyList();
            }
            list = courierMapper.list(null, addressIdList, type, status, urgency, startTime, endTime);
            if (!CollectionUtils.isEmpty(list) && !CollectionUtils.isEmpty(courierIds)) {
                list.removeIf(courier -> !courierIds.contains(courier.getCourierId()));
            }
            return list;
        }
        if (CollectionUtils.isEmpty(courierIds)) {
            return Collections.emptyList();
        }
        return courierMapper.listByCourierIds(courierIds);
    }


    @Override
    public List<Courier> listByStatus(CourierStatusEnum status) {
        return courierMapper.list(null, null, null, status, null, null, null);
    }


    @Override
    public Courier getByCourierId(String courierId) {
        return courierMapper.getByCourierId(courierId);
    }


    @Override
    @Transactional("txManager")
    public void send(String userCity, Long sendAddressId, Long receiveAddressId, Customer sender, Long sendNetworkId,
                     String senderAddress, String receiverName, String receiverPhone, String receiverEmail,
                     Long receiveNetworkId, String receiverAddress, CourierTypeEnum type, UrgencyEnum urgency) {

        //拼接快递寄件人信息
        CourierAddress sendAddress = new CourierAddress();
        if (sendAddressId != null) {
            Address send = addressService.getById(sendAddressId);
            sendAddress.setUsername(send.getName());
            sendAddress.setPhone(send.getPhone());
            sendAddress.setEmail(send.getEmail());
            sendAddress.setNetwork(send.getNetwork());
            sendAddress.setAddress(getAddress(send.getNetwork().getArea(), send.getAddress()));
        } else {
            sendAddress.setUsername(sender.getName());
            sendAddress.setPhone(sender.getPhone());
            sendAddress.setEmail(sender.getEmail());
            Network network = networkCache.getNetWorkById(sendNetworkId);
            if (network == null) {
                throw new ServiceException("未找到寄件网点");
            }
            sendAddress.setNetwork(network);
            sendAddress.setAddress(getAddress(network.getArea(), senderAddress));
        }
        if (StringUtils.isBlank(userCity) || !sendAddress.getAddress().contains(userCity)) {
            throw new ServiceException("寄件网点离您当前位置过远，请重新选择您附近的寄件网点");
        }

        //拼接快递收件人信息
        CourierAddress receiveAddress = new CourierAddress();
        if (receiveAddressId != null) {
            Address receive = addressService.getById(receiveAddressId);
            receiveAddress.setUsername(receive.getName());
            receiveAddress.setNetwork(receive.getNetwork());
            receiveAddress.setPhone(receive.getPhone());
            receiveAddress.setEmail(receive.getEmail());
            receiveAddress.setAddress(getAddress(receive.getNetwork().getArea(), receive.getAddress()));
        } else {
            receiveAddress.setUsername(receiverName);
            receiveAddress.setPhone(receiverPhone);
            receiveAddress.setEmail(receiverEmail);
            Network network = networkCache.getNetWorkById(receiveNetworkId);
            if (network == null) {
                throw new ServiceException("未找到收件网点");
            }
            receiveAddress.setNetwork(network);
            receiveAddress.setAddress(getAddress(network.getArea(), receiverAddress));
        }
        if (Objects.equals(sendAddress.getNetwork().getId(), receiveAddress.getNetwork().getId())) {
            throw new ServiceException("寄件网点和收件网点相同，请重新选择");
        }

        //添加快递地址
        courierAddressMapper.insert(sendAddress);
        courierAddressMapper.insert(receiveAddress);
        //添加快递
        courierMapper.insert(Sn.next(), sendAddress.getId(), receiveAddress.getId(), type,
                CourierStatusEnum.UNCOLLECTED, urgency);
    }


    @Override
    public void update(String courierId, Double weight, CourierStatusEnum status, Long logisticsId, String endTime,
                       boolean info) {
        if (status == CourierStatusEnum.ARRIVED) {
            if (redisClient.hasKey(courierId)) {
                //redis里面存在该快递单号，表示该快递代签收，签收后删除redis中该快递单号
                redisClient.delete(courierId);
                List<CourierDelivery> list = courierDeliveryService.list(null, courierId, DeliveryStatusEnum.DELIVERING,
                        null, null, null);
                if (!CollectionUtils.isEmpty(list)) {
                    SysUser sysUser = list.get(0).getDeliveryMan();
                    courierDeliveryService.updateStatus(sysUser.getId(), courierId, DeliveryStatusEnum.DELIVERED,
                            endTime);
                }
            } else {
                //不允许直接调用二维码签收接口
                throw new ServiceException("非法访问");
            }
        }
        Courier courier = courierMapper.getByCourierId(courierId);
        if (courier == null) {
            throw new ServiceException("未查找到该快递单号的快递");
        }
        BigDecimal amount = null;
        if (weight != null) {
            BigDecimal price = courierMapper.queryPrice(courier.getType());
            amount = BigDecimal.valueOf(weight).multiply(price);
            if (courier.getUrgency() == UrgencyEnum.QUICK) {
                //特快双倍运费
                amount = amount.multiply(BigDecimal.valueOf(2));
            }
        }
        courierMapper.update(courierId, weight, amount, status, logisticsId, endTime);

        if (!info && status != CourierStatusEnum.DELIVERY) {
            //快递为派送状态一定通知
            return;
        }

        String content = COURIER_STATUS_EMAIL_CONTENT + status.getDesc();
        //添加查看详情按钮
        content += URGE_BUTTON.replace(COURIER_ID, courierId).replace(BUTTON, "查看详情");

        if (status == CourierStatusEnum.DELIVERY) {
            //状态为派送中，则给收件人发送签收二维码
            ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
            QRCodeHelper.buildQRCodeToBufferedImage(160, 160, QRCODE_API + courierId, outputStream);
            content += QRCODE_CONTENT.replace(IMAGE_CODE, Base64.encodeBase64String(outputStream.toByteArray()));
            //添加加急按钮
            content += URGE_BUTTON.replace(COURIER_ID, courierId).replace(BUTTON, "催单");
            //往redis里面设置该快递信息，表示该快递待签收
            redisClient.opsForValue().setIfAbsent(courierId, "");
        }

        //状态更新，则给收件人发邮件
        emailHelper.sendMail(courier.getReceiver().getEmail(), COURIER_STATUS_EMAIL_TITLE, content);
    }


    @Override
    public void allot(String courierId, SysUser deliveryMan) {
        Courier courier = courierMapper.getByCourierId(courierId);
        if (courier == null) {
            throw new ServiceException("未查到快递");
        }
        Long path = null;
        if (courier.getStatus() == CourierStatusEnum.UNCOLLECTED) {
            //如果快递是待揽件状态，则该快递员送达的目的地应为该快递员的网点
            path = deliveryMan.getNetwork().getId();
        } else {
            if (!Objects.equals(courier.getReceiver().getNetwork().getId(), deliveryMan.getNetwork().getId())) {

                if (courier.getUrgency() == UrgencyEnum.NORMAL) {
                    List<Long> pathList = networkCache.getPath(deliveryMan.getNetwork().getId(),
                            courier.getReceiver().getNetwork().getId());
                    //如果没有找到路径，则派送地址直接填写收件网点
                    path = CollectionUtils.isEmpty(pathList) ? courier.getReceiver().getNetwork().getId()
                            : pathList.get(1);
                } else {
                    //如果是特快，则下个目的网点直接是收件网点
                    path = courier.getReceiver().getNetwork().getId();
                }
            }

        }
        courierDeliveryService.insert(courierId, deliveryMan.getId(), path);
    }


    @Override
    public BigDecimal queryPrice(CourierTypeEnum type) {
        return courierMapper.queryPrice(type);
    }


    @Override
    public CourierDeliveryPathDto getDeliveryPath(String courierId) {
        Courier courier = courierMapper.getByCourierId(courierId);
        Long start = courier.getLogistics() == null ? courier.getSender().getNetwork().getId()
                : courier.getLogistics().getNetwork().getId();
        Long end = courier.getReceiver().getNetwork().getId();

        CourierDeliveryPathDto dto = new CourierDeliveryPathDto();
        dto.setDay(networkCache.getDay(start, end));
        List<Long> path = networkCache.getPath(start, end);
        dto.setPath(networkCache.getNetWorkByNetworkIds(path));
        return dto;
    }


    @Override
    public List<Courier> listByCourierIds(List<String> courierIds) {
        return courierMapper.listByCourierIds(courierIds);
    }


    @Override
    public void urge(String courierId) {
        if (redisClient.hasKey(RedisKey.urgeCourierKey(courierId))) {
            throw new ServiceException("您已催单，请勿重复催单");
        }

        Courier courier = courierMapper.getByCourierId(courierId);
        if (courier == null || courier.getStatus() == CourierStatusEnum.ARRIVED) {
            return;
        }
        if (courier.getStatus() != CourierStatusEnum.DELIVERY) {
            Long startNetwork = courier.getLogistics() == null ? courier.getSender().getNetwork().getId()
                    : courier.getLogistics().getNetwork().getId();
            Long endNetwork = courier.getReceiver().getNetwork().getId();
            if (Objects.equals(startNetwork, endNetwork)) {
                throw new ServiceException("当前快递非派送状态，请耐心等待,预计今天送达");
            }
            Integer day = networkCache.getDay(startNetwork, endNetwork);
            throw new ServiceException("当前快递非派送状态，请耐心等待" + (day == null ? "" : ",预计还需 " + day + " 天"));
        }
        List<CourierDelivery> list = courierDeliveryService.list(null, courierId, DeliveryStatusEnum.DELIVERING, null,
                null, null);
        if (CollectionUtils.isEmpty(list)) {
            //都已经是派送状态了，不可能为空，但还是判断一下，毕竟鬼知道谁会乱造数据给你说是个bug呢
            return;
        }
        //派送中理论在时间上只会有一个快递员在派送
        CourierDelivery courierDelivery = list.get(0);
        DingDingRobot.delivery().send("快递加急", "客户催促加急派送!", Collections.singletonList("快递单号：" + courierId),
                Collections.singletonList(courierDelivery.getDeliveryMan().getPhone()), null);
        //30分钟内不允许催促第二次
        redisClient.opsForValue().set(RedisKey.urgeCourierKey(courierId), "", 30, TimeUnit.MINUTES);
    }


    /** 拼接地址 **/
    private String getAddress(Area area, String address) {
        City city = area.getCity();
        Province province = city.getProvince();
        return province.getName() + city.getName() + area.getName() + address;
    }
}
